/*
 * Copyright © 2004-2014 chenYuan. All rights reserved.
 * @Website:wwww.jspx.net
 * @Mail:39793751@qq.com
 * @author: chenYuan , 陈原
 * @License: Jspx.net Framework Code is open source (LGPL)，Jspx.net Framework 使用LGPL 开源授权协议发布。
 * @jvm:jdk1.6+  x86/amd64
 *
 */
package com.jspx.sober.impl;

import com.jspx.sober.SoberEnv;
import com.jspx.sober.SoberFactory;
import com.jspx.sober.SqlMapClient;
import com.jspx.sober.TableModels;
import com.jspx.sober.jdbc.JdbcOperations;
import com.jspx.sober.util.JdbcUtil;
import com.jspx.sober.util.DataMap;
import com.jspx.sober.config.MapSQL;
import com.jspx.sober.config.SQLRoom;
import com.jspx.sober.dialect.Dialect;
import com.jspx.utils.ClassUtil;
import com.jspx.utils.StringUtil;
import com.jspx.utils.BeanUtil;
import java.sql.*;
import java.util.*;
import java.io.IOException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Created by IntelliJ IDEA.
 * User:chenYuan (mail:39793751@qq.com)
 * Date: 2007-1-9
 * Time: 20:41:00
 * Ibatis 的 SqlMapClient 方式扩展
 */
public class SqlMapClientImpl implements SqlMapClient {
    final private static Logger log = LoggerFactory.getLogger(SqlMapClientImpl.class);
    private SoberFactory soberFactory;
    private JdbcOperations jdbcOperations;

    public SqlMapClientImpl(JdbcOperations jdbcOperations) {
        this.jdbcOperations = jdbcOperations;
        this.soberFactory = jdbcOperations.getSoberFactory();
    }

    /**
     * @param namespace 命名空间
     * @param exeid     sqlId
     * @param o         参数对象
     * @return 返回单一对象
     * @throws Exception
     */
    public Object getUniqueResult(String namespace, String exeid, Object o) throws Exception {
        Map<String, Object> valueMap = BeanUtil.toMap(o);
        if (o != null) {
            TableModels soberTable = jdbcOperations.getSoberTable(o.getClass());
            valueMap.put(Dialect.KEY_TABLE_NAME, soberTable.getTableName());
            valueMap.put(Dialect.KEY_PRIMARY_KEY, soberTable.getPrimary());
        }
        return getUniqueResult(namespace, exeid, valueMap);
    }

    /**
     * 单个对象查询返回
     *
     * @param namespace 命名空间
     * @param exeid     sql ID
     * @param valueMap  查询参数
     * @return Object
     * @throws Exception
     */
    public Object getUniqueResult(String namespace, String exeid, Map<String, Object> valueMap) throws Exception {
        SQLRoom sqlRoom = soberFactory.getSQLRoom(namespace);
        MapSQL mapSQL = sqlRoom.getQueryMap().get(exeid);
        if (mapSQL == null) return null;
        Dialect dialect = soberFactory.getDialect();
        return jdbcOperations.getUniqueResult(dialect.processSQL(mapSQL.getContext(), valueMap));
    }
    /**
     * ibatis 方式查询返回
     *
     * @param namespace   命名空间
     * @param exeid       查询语句ID
     * @param o           查询对象条件
     * @param currentPage 当前页数
     * @param totalCount  最大行数
     * @param loadChild   是否载入
     * @param rollRows    是否滚动
     * @return List
     * @throws Exception
     */
    public List queryForList(String namespace, String exeid, Object o, int currentPage, int totalCount, boolean loadChild, boolean rollRows) throws Exception {
        if (totalCount>jdbcOperations.getMaxRows()) totalCount = jdbcOperations.getMaxRows();
        Map<String, Object> valueMap = BeanUtil.toMap(o);
        if (o != null) {
            TableModels soberTable = jdbcOperations.getSoberTable(o.getClass());
            valueMap.put(Dialect.KEY_TABLE_NAME, soberTable.getTableName());
            valueMap.put(Dialect.KEY_PRIMARY_KEY, soberTable.getPrimary());
        }
        return queryForList(namespace, exeid, valueMap, currentPage, totalCount, loadChild, rollRows);
    }

    /**
     * 模版方式查询
     * 如果不想缓存数据，在数据使用后clear就可以了
     *
     * @param namespace   命名空间
     * @param exeId       查询ID
     * @param valueMap    参数MAP
     * @param currentPage 第几页
     * @param totalCount  一页的行数
     * @param loadChild   是否载入映射对象
     * @param rollRows    是否让程序执行滚动,如果不让程序执行滚动，那么在程序里边要自己判断滚动
     * @return 查询列表
     * @throws Exception
     */
    @SuppressWarnings("unchecked")
    public List queryForList(String namespace, String exeId, Map<String, Object> valueMap, int currentPage, int totalCount, boolean loadChild, boolean rollRows) throws Exception {

        if (totalCount>jdbcOperations.getMaxRows()) totalCount = jdbcOperations.getMaxRows();
        if (currentPage <= 0) currentPage = 1;
        int beginRow = currentPage * totalCount - totalCount;
        if (beginRow < 0) beginRow = 0;
        int endRow = beginRow + totalCount;

        SQLRoom sqlRoom = soberFactory.getSQLRoom(namespace);
        if (sqlRoom == null) {
            log.error("ERROR:not get sql map namespace " + namespace + ",sql映射中不能够得到相应的命名空间");
            return new ArrayList<Object>();
        }
        Dialect dialect = soberFactory.getDialect();
        MapSQL mapSQL = sqlRoom.getQueryMap().get(exeId);
        if (mapSQL == null) {
            log.error("ERROR:not get sql map namespace " + namespace + " and id " + exeId + ",此命名空间中不能够找到sql");
            return new ArrayList<Object>();
        }
        valueMap.put("databaseName", soberFactory.getDatabaseName());
        valueMap.put("currentPage", currentPage);
        valueMap.put("totalCount", totalCount);
        valueMap.put("loadChild", loadChild);
        valueMap.put("rollRows", rollRows);
        valueMap.put("beginRow", beginRow);
        valueMap.put("endRow", endRow);
        valueMap.put("namespace", namespace);
        String sqlText = StringUtil.empty;
        Connection conn = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;
        List list = null;

        try {
            sqlText = dialect.processSQL(mapSQL.getContext(), valueMap);
            if (StringUtil.isNULL(sqlText))
            {
                throw new Exception("ERROR SQL IS NULL");
            }

            jdbcOperations.debugPrint(sqlText);
            conn = jdbcOperations.getConnection(SoberEnv.ReadOnly);
            //结果集的游标只能向下滚动  并且为只读模式

            if (!dialect.supportsConcurReadOnly())
                preparedStatement = conn.prepareStatement(sqlText);
            else
                preparedStatement = conn.prepareStatement(sqlText,ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);

            preparedStatement.setMaxRows(endRow);
            resultSet = preparedStatement.executeQuery();

            ResultSetMetaData resultSetMetaData = resultSet.getMetaData();
            int numColumns = resultSetMetaData.getColumnCount();
            if (rollRows&&beginRow>0) {
                resultSet.absolute(beginRow);
            }
            list = new ArrayList();
            while (resultSet.next()) {
                if (StringUtil.isNULL(mapSQL.getResultClass()) || "map".equalsIgnoreCase(mapSQL.getResultClass())) {
                    Map<String, Object> resultMap = new DataMap<String, Object>();
                    for (int c = 1; c <= numColumns; c++) {
                        String colName = resultSetMetaData.getColumnLabel(c);
                        resultMap.put(colName, dialect.getResultSetValue(resultSet, c));
                    }
                    list.add(resultMap);
                } else if ("list".equalsIgnoreCase(mapSQL.getResultClass())) {
                    list.add(dialect.getResultSetValue(resultSet, 1));
                }
                else
                {
                    Object resultObject = ClassUtil.newInstance(mapSQL.getResultClass());
                    TableModels soberTable = soberFactory.getTableModels(resultObject.getClass(), jdbcOperations);
                    if (soberTable!=null)
                         resultObject = jdbcOperations.loadColumnsValue(soberTable, resultSet);
                    else
                    {
                         resultObject = JdbcUtil.getBean(resultSet,resultObject.getClass(),true,dialect);
                    }
                    if (loadChild) jdbcOperations.loadNexusValue(soberTable, resultObject);
                    list.add(resultObject);
                }
                if (list.size() > totalCount) break;
            }
        } catch (Exception e) {
            log.error("ERROR SQL:" + sqlText, e);
            e.printStackTrace();
            soberFactory.closeConnection(conn,true);
        } finally {
            JdbcUtil.closeResultSet(resultSet);
            JdbcUtil.closeStatement(preparedStatement);
            jdbcOperations.closeConnection(conn);
        }
        //放入cache
        return list;
    }

    /**
     * 
     * @param namespace 命名空间
     * @param exeid 执行id
     * @param o 对象参数
     * @return 是否执行成功
     * @throws IOException
     */
    public boolean execute(String namespace, String exeid, Object o) throws IOException {
        Map<String, Object> valueMap = BeanUtil.toMap(o);
        if (o != null) {
            TableModels soberTable = jdbcOperations.getSoberTable(o.getClass());
            valueMap.put(Dialect.KEY_TABLE_NAME, soberTable.getTableName());
            valueMap.put(Dialect.KEY_PRIMARY_KEY, soberTable.getPrimary());
        }
        return execute(namespace, exeid, valueMap);
    }

    /**
     * ibatis model execute
     * @param namespace 命名空间
     * @param exeid     sql  Id
     * @param valueMap  参数map
     * @return boolean
     */
    public boolean execute(String namespace, String exeid, Map<String, Object> valueMap) throws IOException {
        SQLRoom sqlRoom = soberFactory.getSQLRoom(namespace);
        Dialect dialect = soberFactory.getDialect();
        MapSQL mapSQL = sqlRoom.getExecuteMap().get(exeid);
        if (mapSQL == null) return false;
        try {
            return jdbcOperations.execute(dialect.processSQL(mapSQL.getContext(), valueMap));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * @param namespace 命名空间
     * @param exeid 执行id
     * @param o 对象参数
     * @return 更新是否成功
     * @throws IOException
     */
    public int update(String namespace, String exeid, Object o) throws IOException {
        Map<String, Object> valueMap = BeanUtil.toMap(o);
        if (o != null) {
            TableModels soberTable = jdbcOperations.getSoberTable(o.getClass());
            valueMap.put(Dialect.KEY_TABLE_NAME, soberTable.getTableName());
            valueMap.put(Dialect.KEY_PRIMARY_KEY, soberTable.getPrimary());
        }
        return update(namespace, exeid, valueMap);
    }

    /**
     * 
     * @param namespace 命名空间
     * @param exeid 执行id
     * @param valueMap 参数msp
     * @return 更新是否成功
     * @throws IOException
     */
    public int update(String namespace, String exeid, Map<String, Object> valueMap) throws IOException {
        SQLRoom sqlRoom = soberFactory.getSQLRoom(namespace);
        Dialect dialect = soberFactory.getDialect();
        MapSQL mapSQL = sqlRoom.getUpdateMap().get(exeid);
        if (mapSQL == null) {
            String error = "ERROR SQL map not config SQL update id :" + exeid + "  namespace:" + namespace;
            log.error(error);
            throw new IOException(error);
        }
        try {
            return jdbcOperations.update(dialect.processSQL(mapSQL.getContext(), valueMap));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return -2;
    }
}